﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.IO;

namespace Ganaye.EmbeddedDb
{
    public interface ISimpleSerializable
    {
        int SerializationBufferSize();
        void Serialize(SimpleSerializer context);
    }
    
    public abstract class SimpleSerializer
    {
        public abstract void SerializeByte(ref byte value);
        public abstract void SerializeChar(ref char value);
        public abstract void SerializeYMD(ref DateTime value);
        public abstract void SerializeUInt32(ref uint value);
        public abstract void SerializeUInt16(ref ushort value);
        public abstract void SerializeString(ref string value, int size, char filler);
        public abstract void Skip(int nbBytes);
        public abstract bool IsFromBytes();

        public static void SerializeFromBytes(IHasEncoding encoding, Byte[] bytes, ISimpleSerializable record)
        {
            var serializer = new FromBytesSerializer(encoding, bytes);
            record.Serialize(serializer);
        }

        public static byte[] SerializeToBytes(IHasEncoding encoding, ISimpleSerializable record)
        {
            var serializer = new ToBytesSerializer(encoding);
            record.Serialize(serializer);
            byte[] bytes=serializer.Bytes;
#if ASSERTS
            System.Diagnostics.Debug.Assert(record.SerializationBufferSize == bytes.Length,"Invalid Buffer size");
#endif
            return bytes;
        }

        private class FromBytesSerializer : SimpleSerializer
        {
            private BinaryReader br;
            private IHasEncoding mEncoding;

            public FromBytesSerializer(IHasEncoding encoding, byte[] bytes)
            {
                mEncoding = encoding;
                br = new BinaryReader(new MemoryStream(bytes));
            }

            public override void SerializeByte(ref byte value)
            {
                value = br.ReadByte();
            }

            public override void SerializeChar(ref Char value)
            {
                value = (Char)br.ReadByte();
            }

            public override void SerializeYMD(ref DateTime value)
            {
                int year = br.ReadByte() + 1900;
                byte month = br.ReadByte();
                byte day = br.ReadByte();
                try
                {
                    value = new DateTime(year, month, day);
                }
                catch
                {
                    value = DateTime.MinValue;
                }
            }

            public override void SerializeUInt32(ref uint value)
            {
                value = br.ReadUInt32();
            }

            public override void SerializeUInt16(ref ushort value)
            {
                value = br.ReadUInt16();
            }

            public override void SerializeString(ref string value, int size,char filler)
            {
                Byte[] bytes = br.ReadBytes(size);
                int last = bytes.Length - 1;
                while (last >= 0 && bytes[last] == 0) last--;
                value = (last < 0 ? null : mEncoding.Encoding.GetString(bytes, 0, last + 1).Trim());
            }

            public override void Skip(int nbBytes)
            {
                br.BaseStream.Seek(nbBytes, SeekOrigin.Current);
            }

            public override bool IsFromBytes()
            {
                return true;
            }
        }

        private class ToBytesSerializer : SimpleSerializer
        {
            private BinaryWriter bw;
            private MemoryStream mem;
            private IHasEncoding mEncoding;

            public ToBytesSerializer(IHasEncoding encoding)
            {
                mEncoding = encoding;
                mem = new MemoryStream(512);
                bw = new BinaryWriter(mem);
            }

            public override void SerializeByte(ref byte value)
            {
                bw.Write(value);
            }

            public override void SerializeChar(ref char value)
            {
                bw.Write(value);
            }

            public override void SerializeYMD(ref DateTime value)
            {
                byte[] ymd = new byte[3];
                ymd[0] = (byte)(value.Year - 1900);
                ymd[1] = (byte)(value.Month);
                ymd[2] = (byte)(value.Day);
                bw.Write(ymd);
            }

            public override void SerializeUInt32(ref uint value)
            {
                bw.Write(value);
            }

            public override void SerializeUInt16(ref ushort value)
            {
                bw.Write(value);
            }

            public override void SerializeString(ref string value, int size, char filler)
            {
                var bytes = mEncoding.Encoding.GetBytes(value);
                int valueLength = bytes.Length;
                if (valueLength >= size)
                {
                    bw.Write(bytes, 0, size);
                }
                else 
                {

                    bw.Write(bytes, 0, valueLength);
                    while (valueLength < size)
                    {
                        bw.Write((byte)filler);
                        valueLength++;
                    }
                    
                }
                //mE
                //Byte[] bytes =mEncoding.
                //int last = bytes.Length - 1;
                //while (last >= 0 && bytes[last] == 0) last--;
                //value = (last < 0 ? null : mEncoding.Encoding.GetString(bytes, 0, last + 1).Trim());
                //sadasd
                //bw.Write(value);
            }

            public override void Skip(int nbBytes)
            {
                for (int i = 0; i < nbBytes; i++)
                {
                    bw.Write((byte)0);
                }
            }

            public override bool IsFromBytes()
            {
                return false;
            }

            public byte[] Bytes
            {
                get { return mem.ToArray(); }
            }
        }
    }

}
/*

        public SimpleSerializer(byte[] buffer, bool writeToBuffer)
        {
            if (writeToBuffer) 
                bw = new System.IO.BinaryWriter(new System.IO.MemoryStream(buffer));
            else
                br = new System.IO.BinaryReader(new System.IO.MemoryStream(buffer));
        }

        internal void SerializeByte(ref byte newValue)
        {
            if (bw != null)
                bw.Write(newValue);
            else
                newValue = br.ReadByte();
        }

        internal void SerializeYMD(ref DateTime newValue)
        {
            if (bw != null)
            {
                if (newValue == DateTime.MinValue)
                {
                    bw.Write((byte)0);
                    bw.Write((byte)0);
                    bw.Write((byte)0);
                }
                else
                {
                    bw.Write((byte)(newValue.Year % 100));
                    bw.Write((byte)newValue.Month);
                    bw.Write((byte)newValue.Day);
                }
            }
            else
            {
            }
        }

        internal void SerializeUInt32(ref uint newValue)
        {
            throw new NotImplementedException();
        }

        internal void SerializeUInt16(ref ushort newValue)
        {
            throw new NotImplementedException();
        }

        internal void SerializeString(ref string newValue, int size)
        {
            throw new NotImplementedException();
        }

        internal void Skip(int nbBytes)
        {
            throw new NotImplementedException();
        }
 * */